﻿using System;
using System.Runtime.InteropServices;

namespace TestSpyApi
{
    class ListViewWinAPI
    {
        private const uint LVM_FIRST = 0x1000;
        private const uint LVM_GETHEADER = LVM_FIRST + 31;
        private const uint LVM_GETITEMCOUNT = LVM_FIRST + 4;//获取列表行数
        private const uint LVM_GETITEMTEXT = LVM_FIRST + 45;//获取列表内的内容
        private const uint LVM_GETITEMW = LVM_FIRST + 75;
        private const uint LVM_GETITEMRECT = LVM_FIRST + 14;
        private const uint LVM_GETITEMPOSITION = LVM_FIRST + 16;


        private const uint HDM_GETITEMCOUNT = 0x1200;//获取列表列数

        private const uint PROCESS_VM_OPERATION = 0x0008;//允许函数VirtualProtectEx使用此句柄修改进程的虚拟内存
        private const uint PROCESS_VM_READ = 0x0010;//允许函数访问权限
        private const uint PROCESS_VM_WRITE = 0x0020;//允许函数写入权限

        private const uint MEM_COMMIT = 0x1000;//为特定的页面区域分配内存中或磁盘的页面文件中的物理存储
        private const uint MEM_RELEASE = 0x8000;
        private const uint MEM_RESERVE = 0x2000;//保留进程的虚拟地址空间,而不分配任何物理存储

        private const uint PAGE_READWRITE = 4;

        private const int LVIF_TEXT = 0x0001;

        private const int LVIR_SELECTBOUNDS = 3;

        [DllImport("user32.DLL")]
        private static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
        [DllImport("user32.dll")]//找出某个窗口的创建者(线程或进程),返回创建者的标志符
        private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint dwProcessId);
        [DllImport("kernel32.dll")]//打开一个已存在的进程对象,并返回进程的句柄
        private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);
        [DllImport("kernel32.dll")]//为指定的进程分配内存地址:成功则返回分配内存的首地址
        private static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
        [DllImport("kernel32.dll")]//从指定内存中读取字节集数据
        private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, int nSize, ref uint vNumberOfBytesRead);
        [DllImport("kernel32.dll")]//将数据写入内存中
        private static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, int nSize, ref uint vNumberOfBytesRead);
        [DllImport("kernel32.dll")]
        private static extern bool CloseHandle(IntPtr handle);
        [DllImport("kernel32.dll")]//在其它进程中释放申请的虚拟内存空间
        private static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress,
           uint dwSize, uint dwFreeType);

        /// <summary>
        /// LVITEM结构体,是列表视图控件的一个重要的数据结构
        /// 占空间：4(int)x7=28个byte
        /// </summary>
        private struct LVITEM  //结构体
        {
            public int mask;//说明此结构中哪些成员是有效的
            public int iItem;//项目的索引值(可以视为行号)从0开始
            public int iSubItem; //子项的索引值(可以视为列号)从0开始
            public int state;//子项的状态
            public int stateMask; //状态有效的屏蔽位
            public IntPtr pszText;  //主项或子项的名称
            public int cchTextMax;//pszText所指向的缓冲区大小
        }
        public static string GetListItemText(IntPtr tvHandle, int rowIndex, int colIndex)
        {
            uint vProcessId;
            GetWindowThreadProcessId(tvHandle, out vProcessId);

            IntPtr vProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ |
                PROCESS_VM_WRITE, false, vProcessId);
            IntPtr vPointer = VirtualAllocEx(vProcess, IntPtr.Zero, 4096,
                MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

            byte[] vBuffer = new byte[256];//定义一个临时缓冲区
            LVITEM[] vItem = new LVITEM[1];
            vItem[0].mask = LVIF_TEXT;//说明pszText是有效的
            vItem[0].iItem = rowIndex;     //行号
            vItem[0].iSubItem = colIndex;  //列号
            vItem[0].cchTextMax = vBuffer.Length;//所能存储的最大的文本为256字节
            vItem[0].pszText = (IntPtr)((int)vPointer + Marshal.SizeOf(typeof(LVITEM)));
            uint vNumberOfBytesRead = 0;

            //把数据写到vItem中
            //pointer为申请到的内存的首地址
            //UnsafeAddrOfPinnedArrayElement:获取指定数组中指定索引处的元素的地址
            WriteProcessMemory(vProcess, vPointer, Marshal.UnsafeAddrOfPinnedArrayElement(vItem, 0), Marshal.SizeOf(typeof(LVITEM)), ref vNumberOfBytesRead);

            //发送LVM_GETITEMW消息给hwnd,将返回的结果写入pointer指向的内存空间
            SendMessage((int)tvHandle, LVM_GETITEMTEXT, rowIndex, (int)vPointer);

            //从pointer指向的内存地址开始读取数据,写入缓冲区vBuffer中
            ReadProcessMemory(vProcess, (IntPtr)((int)vPointer + Marshal.SizeOf(typeof(LVITEM))), Marshal.UnsafeAddrOfPinnedArrayElement(vBuffer, 0), vBuffer.Length, ref vNumberOfBytesRead);

            //string vText = Encoding.Unicode.GetString(vBuffer, 0, (int)vNumberOfBytesRead);
            string vText = Marshal.PtrToStringAnsi(Marshal.UnsafeAddrOfPinnedArrayElement(vBuffer, 0));

            VirtualFreeEx(vProcess, vPointer, 0, MEM_RELEASE);//在其它进程中释放申请的虚拟内存空间,MEM_RELEASE方式很彻底,完全回收
            CloseHandle(vProcess);//关闭打开的进程对象

            return vText;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }
        public static RECT GetListItemRect(IntPtr tvHandle, int rowIndex)
        {
            uint vProcessId;
            GetWindowThreadProcessId(tvHandle, out vProcessId);

            IntPtr vProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ |
                PROCESS_VM_WRITE, false, vProcessId);

            int size_of_rect = Marshal.SizeOf<RECT>();

            var rects = new RECT[] { new RECT() { left = LVIR_SELECTBOUNDS } };

            IntPtr vPointer = VirtualAllocEx(vProcess, IntPtr.Zero, (uint)size_of_rect,
                MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

            try
            {
                uint vNumberOfBytesRead = 0;
                WriteProcessMemory(vProcess, vPointer, 
                    Marshal.UnsafeAddrOfPinnedArrayElement(rects, 0), size_of_rect, ref vNumberOfBytesRead);

                SendMessage((int)tvHandle, LVM_GETITEMRECT, rowIndex, vPointer.ToInt32());

                ReadProcessMemory(vProcess, vPointer, 
                    Marshal.UnsafeAddrOfPinnedArrayElement(rects, 0), size_of_rect, ref vNumberOfBytesRead);
            }
            finally
            {
                VirtualFreeEx(vProcess, vPointer, 0, MEM_RELEASE);
                CloseHandle(vProcess);
            }

            return rects[0];
        }

    }
}
